package com.example.demo.SecurityConfig;

import com.example.demo.SecurityConfig.LoginConfig.JsonLoginAuthenticationFilter;
import com.example.demo.SecurityConfig.LoginConfig.LoginFailureAuthenticationHandler;
import com.example.demo.SecurityConfig.LoginConfig.LoginSuccessAuthenticationHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.HttpMethod;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Arrays;

@EnableWebSecurity
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter
{
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private AuthenticationProvider authenticationProvider;
    @Qualifier("userDetailServiceImpl")
    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private AuthenticationSuccessHandler successAuthenticationHandler;
    @Autowired
    private AuthenticationFailureHandler failureAuthenticationHandler;
    @Autowired
    private FilterInvocationSecurityMetadataSource urlPathFilterInvocationSecurityMetadataSource;
    @Autowired
    private AccessDecisionManager decisionManager;
    @Autowired
    private AccessDeniedHandler accessDeniedHandler;
    @Autowired
    private LogoutSuccessHandler logoutSuccessHandler;
    @Autowired
    private LogoutHandler logoutHandler;

    @Bean
    public JsonLoginAuthenticationFilter jsonAuthenticationFilter() throws Exception
    {
        JsonLoginAuthenticationFilter jsonLoginAuthenticationFilter = new JsonLoginAuthenticationFilter();
        jsonLoginAuthenticationFilter.setAuthenticationSuccessHandler(new LoginSuccessAuthenticationHandler());
        jsonLoginAuthenticationFilter.setAuthenticationFailureHandler(new LoginFailureAuthenticationHandler());
        jsonLoginAuthenticationFilter.setFilterProcessesUrl("/login");
        jsonLoginAuthenticationFilter.setAuthenticationManager(authenticationManagerBean());
        return jsonLoginAuthenticationFilter;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception
    {
        //super.configure(auth);
        auth.authenticationProvider(authenticationProvider);
        auth.userDetailsService(userDetailsService);
    }

    private HttpMethod httpMethodMapper(String method)
    {
        switch (method)
        {
            case "GET": return HttpMethod.GET;
            case "PUT": return HttpMethod.PUT;
            case "DELETE": return HttpMethod.DELETE;
            case "POST": return HttpMethod.POST;
            case "PATCH": return HttpMethod.PATCH;
            case "HEAD": return HttpMethod.HEAD;
            case "OPTIONS": return HttpMethod.OPTIONS;
            case "TRACE": return HttpMethod.TRACE;
            default: return null;
        }
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception
    {
        //super.configure(http);
        http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
        //http.csrf().disable();
        http.cors();
        File file = null;
        BufferedReader bufferedReader = null;
        try
        {
            file = new ClassPathResource("urlAuthority.settings").getFile();
            bufferedReader = new BufferedReader(new FileReader(file));
            String content;
            while ((content = bufferedReader.readLine()) != null)
            {
                String[] setting = content.split("[ ]+");
                http.authorizeRequests().withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>()
                {
                    @Override
                    public <O extends FilterSecurityInterceptor> O postProcess(O o)
                    {
                        o.setAccessDecisionManager(decisionManager);
                        o.setSecurityMetadataSource(urlPathFilterInvocationSecurityMetadataSource);
                        return o;
                    }
                }).antMatchers(httpMethodMapper(setting[1]), setting[0]).authenticated();
            }
            bufferedReader.close();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        finally
        {
            if (bufferedReader != null)
            {
                try
                {
                    bufferedReader.close();
                }
                catch (IOException e)
                {
                    e.printStackTrace();
                }
            }
        }
        http.formLogin()
                .successHandler(successAuthenticationHandler)
                .failureHandler(failureAuthenticationHandler).permitAll().and()
                .exceptionHandling().accessDeniedHandler(accessDeniedHandler);
        http.addFilterAt(jsonAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
        http.logout().addLogoutHandler(logoutHandler).logoutSuccessHandler(logoutSuccessHandler).permitAll();
    }

    @Bean
    public UrlBasedCorsConfigurationSource corsConfigurationSource()
    {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("*"));
        configuration.setAllowedMethods(Arrays.asList("GET", "POST", "DELETE", "PUT"));
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
}
